﻿namespace Resharper.Abstractions.CodeDom.Statements.Blocks
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using JetBrains.Annotations;
  using JetBrains.ReSharper.Psi.CSharp.Parsing;
  using JetBrains.ReSharper.Psi.CSharp.Tree;
  using JetBrains.ReSharper.Psi.ExtensionsAPI;
  using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
  using JetBrains.ReSharper.Psi.Tree;
  using JetBrains.Text;
  using JetBrains.Util;

  /// <summary>Defines the <see cref="Block"/> class.</summary>
  public class 
    Block : Statement
  {
    #region Constructors and Destructors

    /// <summary>Initializes a new instance of the <see cref="Block"/> class.</summary>
    /// <param name="block">The block.</param>
    internal Block([NotNull] IBlock block) : base(block)
    {
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets the statements.
    /// </summary>
    [NotNull]
    public IEnumerable<Statement> Statements
    {
      get
      {
        var compositeElement = (CompositeElement)this.InternalBlock;

        var l = (IList<ICSharpStatement>)compositeElement.FindListOfChildrenByRole<ICSharpStatement>(102).ToList();

        return l.Select(StatementFactory.GetStatement).ToList();

        // return this.InternalBlock.Statements.Select(StatementFactory.GetStatement).ToList();
      }
    }

    /// <summary>The internal statement field.</summary>
    [NotNull]
    internal IBlock InternalBlock
    {
      get
      {
        return (IBlock)this.InternalTreeNode;
      }
    }

    #endregion

    #region Public Methods

    /// <summary>Adds the blank line after.</summary>
    /// <param name="statement">The statement.</param>
    public void AddBlankLineAfter([NotNull] Statement statement)
    {
      if (statement == null)
      {
        throw new ArgumentNullException("statement");
      }

      var newLineText = new StringBuffer("\r\n");
      ITreeNode newLine = TreeElementFactory.CreateLeafElement(CSharpTokenType.NEW_LINE, newLineText, 0, newLineText.Length);

      LowLevelModificationUtil.AddChildBefore(statement.InternalTreeNode, newLine);
    }

    /// <summary>Adds the statement after.</summary>
    /// <param name="statement">The statement.</param>
    /// <returns>Returns the statement after.</returns>
    [NotNull]
    public Statement AddStatementAfter([NotNull] Statement statement)
    {
      if (statement == null)
      {
        throw new ArgumentNullException("statement");
      }

      var result = this.InternalBlock.AddStatementAfter((ICSharpStatement)statement.InternalStatement, null);

      return StatementFactory.GetStatement(result);
    }

    /// <summary>Adds the statement after.</summary>
    /// <param name="statement">The statement.</param>
    /// <param name="anchor">The anchor.</param>
    /// <returns>Returns the statement after.</returns>
    [NotNull]
    public Statement AddStatementAfter([NotNull] Statement statement, [NotNull] Statement anchor)
    {
      if (statement == null)
      {
        throw new ArgumentNullException("statement");
      }

      if (anchor == null)
      {
        throw new ArgumentNullException("anchor");
      }

      var result = this.InternalBlock.AddStatementAfter((ICSharpStatement)statement.InternalStatement, (ICSharpStatement)anchor.InternalStatement);

      return StatementFactory.GetStatement(result);
    }

    /// <summary>Adds the statement before.</summary>
    /// <param name="statement">The statement.</param>
    /// <returns>Returns the statement before.</returns>
    [NotNull]
    public Statement AddStatementBefore([NotNull] Statement statement)
    {
      var anchor = this.InternalBlock.Statements.FirstOrDefault();

      var result = this.InternalBlock.AddStatementBefore((ICSharpStatement)statement.InternalStatement, anchor);

      return StatementFactory.GetStatement(result);
    }

    /// <summary>Adds the statement before.</summary>
    /// <param name="statement">The statement.</param>
    /// <param name="anchor">The anchor.</param>
    [NotNull]
    public Statement AddStatementBefore([NotNull] Statement statement, [NotNull] Statement anchor)
    {
      if (statement == null)
      {
        throw new ArgumentNullException("statement");
      }

      if (anchor == null)
      {
        throw new ArgumentNullException("anchor");
      }

      var result = this.InternalBlock.AddStatementBefore((ICSharpStatement)statement.InternalStatement, (ICSharpStatement)anchor.InternalStatement);

      return StatementFactory.GetStatement(result);
    }

    /// <summary>Removes the specified statement.</summary>
    /// <param name="statement">The statement.</param>
    public void Remove([NotNull] Statement statement)
    {
      if (statement == null)
      {
        throw new ArgumentNullException("statement");
      }

      this.InternalBlock.RemoveStatement((ICSharpStatement)statement.InternalStatement);
    }

    #endregion
  }
}